---
title: 从 Create React App（CRA）迁移
description: 将现有的 Create React App 项目迁移到 Astro 的提示
sidebar:
  label: Create React App
type: migration
stub: true
framework: Create React App
i18nReady: true
---
import AstroJSXTabs from '~/components/tabs/AstroJSXTabs.astro';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { FileTree, LinkCard, CardGrid } from '@astrojs/starlight/components';
import ReadMore from '~/components/ReadMore.astro';
import Badge from "~/components/Badge.astro"

Astro 的 [React 集成](/zh-cn/guides/integrations-guide/react/) 支持[在 Astro 组件中使用 React 组件](/zh-cn/guides/framework-components/)，甚至包括像 Create React App (CRA) 这样的整个 React 应用程序！

```astro title="src/pages/index.astro"
---
// 导入你的根应用组件
import App from '../cra-project/App.jsx';
---
<!-- 使用客户端指令加载你的应用 -->
<App client:load />
```
<ReadMore>了解如何使用 React Router [在 Astro 中构建单页应用程序（SPA）](https://logsnag.com/blog/react-spa-with-astro) <Badge text="外部" />。</ReadMore>

当你在已安装 React 集成的 Astro 项目中直接添加应用时，许多应用程序将会作为完整的 React 应用程序运行。这是一个快速启动项目并在迁移到 Astro 过程中保持应用功能的绝佳方式。

随着时间的推移，你可以逐步将结构转换为由 `.astro` 和 `.jsx` 组件组合而成的形式。届时，你可能会发现你需要的 React 组件比你想象中的要少！

以下是一些关键概念和迁移策略帮助你入门，也可以继续阅读我们的文档和参与我们的 [Discord 社区](https://astro.build/chat) 以获取更多帮助！

## CRA 和 Astro 的关键相似之处

-  [`.astro` 文件的语法与 JSX 类似](/zh-cn/reference/astro-syntax/#astro-和-jsx-间的差异)。编写 Astro 应该会让你感到熟悉。

-  Astro 使用基于文件的路由，[并允许通过特殊的文件命名来创建动态路由](/zh-cn/guides/routing/#动态路由)。

-  Astro 是[基于组件的](/zh-cn/basics/astro-components/)，你的标记结构在迁移前后将类似。

-  Astro 提供了 [React、Preact 和 Solid 的官方集成](/zh-cn/guides/integrations-guide/react/)，因此你可以使用现有的 JSX 组件。请注意，这意味着在 Astro 中，这些文件的扩展名必须为 `.jsx` 或 `.tsx`。

-  Astro 支持 [安装 NPM 包](/zh-cn/guides/imports/#npm-包)，包括 React 库。许多现有的依赖项在 Astro 中可以正常工作。

## CRA 和 Astro 的关键差异

在用 Astro 重新构建 CRA 站点时，你会注意到一些重要的差异：

-  CRA 是一个使用 `index.js` 作为项目根目录的单页面应用程序。Astro 是一个多页面站点，其中 `index.astro` 是你的首页。

-  [`.astro` 组件](/zh-cn/basics/astro-components/) 不是作为返回页面模板的导出函数而被构建。相反，你需要将代码分割到一个用于 JavaScript 的“代码围栏”和一个专门用于生成的 HTML 的“body”中。

-  [内容驱动](/zh-cn/concepts/why-astro/#内容驱动)：Astro 的设计目标是展示你的内容，并允许你在需要时选择性地启用交互。如果现有的 CRA 应用用于构建高级的客户端交互，那么可能需要使用高级的 Astro 技术来让 `.astro` 组件来完成更具挑战性的复制，例如仪表板。

## 将 CRA 添加到 Astro

通常情况下，你可以直接在新的 Astro 项目中渲染现有的应用程序，而无需更改应用程序的代码。

### 创建一个新的 Astro 项目

通过你的包管理器来运行 `create astro` 命令启动 Astro 的 CLI 向导，并选择一个新的「空」Astro 项目。

<PackageManagerTabs>
  <Fragment slot="npm">
  ```shell
  npm create astro@latest
  ```
  </Fragment>
  <Fragment slot="pnpm">
  ```shell
  pnpm create astro@latest
  ```
  </Fragment>
  <Fragment slot="yarn">
  ```shell
  yarn create astro@latest
  ```
  </Fragment>
</PackageManagerTabs>

### 添加集成和依赖

通过你的包管理器来运行 `astro add` 命令以添加 React 集成。如果你的应用程序使用了其他的支持 `astro add` 命令的包，例如 Tailwind 和 MDX，那么你便能够仅用一个命令就添加它们：

<PackageManagerTabs>
  <Fragment slot="npm">
  ```shell
  npx astro add react
  npx astro add react tailwind mdx
  ```
  </Fragment>
  <Fragment slot="pnpm">
  ```shell
  pnpm astro add react
  pnpm astro add react tailwind mdx
  ```
  </Fragment>
  <Fragment slot="yarn">
  ```shell
  yarn astro add react
  yarn astro add react tailwind mdx
  ```
  </Fragment>
</PackageManagerTabs>

如果你的 CRA 需要其他依赖项（例如 NPM 包），那么请使用命令行单独安装它们，或者将它们手动添加到你的新 Astro 项目的 `package.json` 中，然后运行安装命令。请注意，并非所有的 React 依赖项都能在 Astro 中正常工作。

### 添加现有的应用文件

将现有的 Create React App (CRA) 项目的源文件和文件夹（例如 `components`、`hooks`、`styles` 等）复制到 `src/` 内的一个新文件夹中，并保留其文件结构，以使你的应用程序继续正常工作。请注意，所有的 `.js` 文件扩展名必须重命名为 `.jsx` 或 `.tsx`。

不要包含任何配置文件。你将使用 Astro 自己的 `astro.config.mjs`、`package.json` 和 `tsconfig.json`。

将应用程序的 `public/` 文件夹的内容（例如静态资源）移动到 Astro 的 `public/` 文件夹中。

<FileTree>
- public/
  - logo.png
  - favicon.ico
  - ...
- src/
  - cra-project/
    - App.jsx
    - ...
  - pages/
    - index.astro
- astro.config.mjs
- package.json
- tsconfig.json
</FileTree>

### 渲染你的应用

在 `index.astro` 的 frontmatter 部分导入你的应用程序的根组件，然后在页面模板中渲染 `<App />` 组件：

```astro title="src/pages/index.astro"
---
import App from '../cra-project/App.jsx';
---
<App client:load />
```

:::note[客户端指令]
你的应用程序需要使用 [客户端指令](/zh-cn/reference/directives-reference/#客户端指令) 来实现交互。在你选择启用客户端 JavaScript 之前，Astro 将把你的 React 应用程序呈现为静态 HTML。

而使用 `client:load` 来确保你的应用程序立即从服务器加载，或者使用 `client:only="react"` 来跳过在服务器上的渲染，完全在客户端运行你的应用程序。
:::

## 将 CRA 转换为 Astro

除了 [将现有应用程序添加到 Astro](#将-cra-添加到-astro)，你可能也希望将应用程序本身直接转换为 Astro！

你可以在现有结构的基础上 [使用 Astro 的 HTML 模板组件](/zh-cn/basics/astro-components/) 以重构类似的基于组件的设计，同时导入和包含个别的 React 组件（它们本身可能是完整的应用程序！）来实现群岛交互。

每个迁移的过程都会有所不同，你可以逐步进行迁移，而不会影响你正在工作的应用程序。按照自己的节奏逐渐转换每个部分，从而使应用程序中越来越多的部分由 Astro 组件驱动。

当你转换你的 React 应用程序时，你需要决定哪些 React 组件你将会 [重写为 Astro 组件](#将-jsx-文件转换为-astro-文件)。唯一的限制是 Astro 组件可以导入 React 组件，但 React 组件只能导入其他 React 组件：

```astro title="src/pages/static-components.astro" ins={2,7}
---
import MyReactComponent from '../components/MyReactComponent.jsx';
---
<html>
  <body>
    <h1>Use React components directly in Astro!</h1>
    <MyReactComponent />
  </body>
</html>
```

相比于在 React 组件中导入 Astro 组件而言，你可以在单个 Astro 组件中使用嵌套的 React 组件。

```astro title="src/pages/nested-components.astro" {2,3,5,8,10}
---
import MyReactSidebar from '../components/MyReactSidebar.jsx';
import MyReactButton from '../components/MyReactButton.jsx';
---
<MyReactSidebar>
  <p>Here is a sidebar with some text and a button.</p>
  <div slot="actions">
    <MyReactButton client:idle />
  </div>
</MyReactSidebar>
```

在将你的 CRA 重构为 Astro 项目之前，了解 [Astro 群岛](/zh-cn/concepts/islands/) 和 [Astro 组件](/zh-cn/basics/astro-components/) 可能会对你有所帮助。

### 比较：JSX 与 Astro

比较一下如下的 CRA 组件和相对应的 Astro 组件：

<AstroJSXTabs>
  <Fragment slot="jsx">
    ```jsx title="StarCount.jsx"
    import React, { useState, useEffect } from 'react';
import Header from './Header';
import Footer from './Footer';

const Component = () => {
    const [stars, setStars] = useState(0);
    const [message, setMessage] = useState('');

    useEffect(() => {
        const fetchData = async () => {
            const res = await fetch('https://api.github.com/repos/withastro/astro');
            const json = await res.json();

            setStars(json.stargazers_count || 0);
            setMessage(json.message);
        };

        fetchData();
    }, []);

    return (
        <>
            <Header />
            <p style={{
                backgroundColor: `#f4f4f4`,
                padding: `1em 1.5em`,
                textAlign: `center`,
                marginBottom: `1em`
            }}>Astro has {stars} 🧑‍🚀</p>
            <Footer />
        </>
    )
};

export default Component;
    ```
  </Fragment>
  <Fragment slot="astro">
    ```astro title="StarCount.astro"
    ---
    import Header from "./Header.astro";
    import Footer from "./Footer.astro";
    import "./layout.css";
    const res = await fetch('https://api.github.com/repos/withastro/astro');
    const json = await res.json();
    const message = json.message;
    const stars = json.stargazers_count || 0;
    ---
    <Header />
    <p class="banner">Astro has {stars} 🧑‍🚀</p>
    <Footer />
    <style>
      .banner {
        background-color: #f4f4f4; 
        padding: 1em 1.5em;
        text-align: center;
        margin-bottom: 1em;
      }
    </style>
    ```
  </Fragment>
</AstroJSXTabs>

### 将 JSX 文件转换为 `.astro` 文件

下面是将 CRA（Create React App）的 `.js` 组件转换为 `.astro` 组件的一些建议：

1. 将现有 CRA 组件函数返回的 JSX 作为 HTML 模板的基础。

2. 将任何 [CRA 或 JSX 语法更改为 Astro](#参考将-cra-语法转换为-astro) 或 HTML 的标准规范。这包括 `{children}` 和 `className` 等。

3. 将任何必要的 JavaScript，包括导入语句，移动到 [「代码围栏」（`---`）](/zh-cn/basics/astro-components/#组件脚本) 中。注意：在 Astro 中，[条件性渲染内容](/zh-cn/reference/astro-syntax/#动态-html)的 JavaScript 通常直接写在 HTML 模板中。

4. 使用 [`Astro.props`](/zh-cn/reference/api-reference/#props) 访问之前传递给 CRA 函数的任何其他属性。

5. 决定是否需要将任何导入的组件转换为 Astro。你可以将它们暂时保留为 React 组件，或永久保留为 React 组件。但是，如果它们不需要交互性，你可能最终会想将它们转换为 `.astro` 组件！

6. 将 `useEffect()` 替换为导入语句或 [`import.meta.glob()`](/zh-cn/guides/imports/#importmetaglob) 来查询本地文件，使用 `fetch()` 来获取外部数据。

### 迁移测试

由于 Astro 输出原始的 HTML，因此可以使用构建步骤的输出来编写端到端测试。如果之前编写的端到端测试能够匹配 CRA 站点的标记，那么它们可能也可以直接使用。在 Astro 中，也可以导入和使用测试库（如 Jest 和 React Testing Library）来测试你的 React 组件。

了解更多详情，请参阅 Astro 的 [测试指南](/zh-cn/guides/testing/)。

## 参考：将 CRA 语法转换为 Astro

### 在 Astro 中转换 CRA 导入语句

将任何 [文件导入](/zh-cn/guides/imports/) 准确地更新为相对引用的文件路径。可以通过使用 [导入别名](/zh-cn/guides/typescript/#导入别名) 或完整写出相对路径来实现。

注意，`.astro` 和其他一些文件类型必须使用它们的完整文件扩展名进行导入。

```astro title="src/pages/authors/Fred.astro"
---
import Card from `../../components/Card.astro`;
---
<Card />
```

### 在 Astro 中转换 CRA 子组件属性

将任何 `{children}` 实例转换为 Astro 的 `<slot />`。Astro 不需要将 `{children}` 作为函数属性来传递，它会自动在 `<slot />` 中渲染子组件内容。

```astro title="src/components/MyComponent.astro" del={3-9} ins={11-13}
---
---
export default function MyComponent(props) { 
    return (
      <div>
        {props.children}
      </div>
    );  
}

<div>
  <slot />
</div>
```

传递多组子元素的 React 组件可以通过使用 [命名插槽](/zh-cn/basics/astro-components/#命名插槽) 转换为 Astro 组件。

了解更多有关于 [特定 `<slot />` 用法](/zh-cn/basics/astro-components/#插槽) 的内容。

### 在 Astro 中转换 CRA 数据获取

在 Create React App 组件中获取数据与 Astro 类似，只有一些细微的差别。

你需要移除任何副作用钩子（`useEffect`）的实例，然后使用 `import.meta.glob()` 或 `getCollection()`（或 `getEntry()`）从项目源代码的其他文件中获取数据。

要 [获取远程数据](/zh-cn/guides/data-fetching/)，请使用 `fetch()`。

这些数据请求是在 Astro 组件的 frontmatter 中进行的，并使用顶层的 await。

```astro title="src/pages/index.astro"
---
import { getCollection } from 'astro:content';

// 从 `src/content/blog/` 目录中获取所有条目
const allBlogPosts = await getCollection('blog');

// 从 `src/pages/posts/` 目录中获取所有条目
const allPosts = Object.values(import.meta.glob('../pages/posts/*.md', { eager: true }));

// 获取远程数据
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
const randomUser = data.results[0];
---
```

了解更多有关于 [使用 `import.meta.glob()`](/zh-cn/guides/imports/#importmetaglob) 导入本地文件、 [使用集合 API 查询](/zh-cn/guides/content-collections/#查询集合) 以及 [获取远程数据](/zh-cn/guides/data-fetching/) 的内容。

### 在 Astro 中转换 CRA 样式

你可能需要用 Astro 中其他可用的 CSS 选项来替换任何 [CSS-in-JS 库](https://github.com/withastro/astro/issues/4432)（例如 styled-components）。

如有必要，将任何行内样式对象（`style={{ fontWeight: "bold" }}`）转换为行内的 HTML 样式属性（`style="font-weight:bold;"`）。或者，使用 Astro 的 `<style>` 标签来实现作用域限定的 CSS 样式。

```astro title="src/components/Card.astro" del={1} ins={2}
<div style={{backgroundColor: `#f4f4f4`, padding: `1em`}}>{message}</div>
<div style="background-color: #f4f4f4; padding: 1em;">{message}</div>
```

如果已安装了 [Tailwind Vite 插件](/zh-cn/guides/styling/#tailwind)，则可以支持 Tailwind，并且不需要更改现有的 Tailwind 代码！

了解更多有关于 [Astro 中的样式](/zh-cn/guides/styling/) 的内容。

## 故障排除

你的 CRA 可能可以在 Astro 中正常工作！但是，你可能需要进行微小调整，以复制现有应用程序的功能和（或）样式。

如果在文档中未找到答案，请访问 [Astro 的 Discord](https://astro.build/chat)，并在我们的支持论坛上提问！

## 社区资源

<CardGrid>

  <LinkCard title="代码修复：SIBA 网站从 Create-React-App 迁移至 Astro" href="https://brittanisavery.com/post/move-siba-to-astro"/>

</CardGrid>

:::note[有想要分享的资源吗？]
如果你找到（或制作）了关于将 Create React App 转换为 Astro 的有用的视频或博客文章，请将其 [添加到这个列表中](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-create-react-app.mdx)！
:::
